package org.zt.java.dal.datasource;

import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.util.StringUtils;
import org.zt.java.dal.config.DalConfig;
import org.zt.java.dal.config.DalConfigLoader;
import org.zt.java.dal.config.vo.DataSourceConfig;
import org.zt.java.dal.config.vo.DataSourceConfigItem;
import org.zt.java.dal.loadbalance.LoadBalanceType;
import org.zt.java.dal.loadbalance.impl.ConsistentHashSlaveLoadBalance;
import org.zt.java.dal.loadbalance.impl.RandomSlaveLoadBalance;
import org.zt.java.dal.router.impl.DefaultDataSourceRouter;


public class DynamicDataSourceHelper {

	private DynamicDataSource dynamicDataSource;
	private ApplicationContext applicationContext;

	public DynamicDataSourceHelper(DynamicDataSource dynamicDataSource, ApplicationContext applicationContext) {
		this.dynamicDataSource = dynamicDataSource;
		this.applicationContext = applicationContext;
	}

	public void loadAndParseDalConfig() {
		DalConfigLoader dalConfigLoader = new DalConfigLoader();
		DalConfig dalConfig = dalConfigLoader.load(dynamicDataSource.getAddress());
		this.dynamicDataSource.setDalConfig(dalConfig);
	}

	public void addDataSourceBeanToSpringContainer() {
		// 注册主数据源
		this.registerDataSourceBean(
				this.dynamicDataSource.getDalConfig().getDataSourceConfig().getMasterDatasourceConfig());

		// 注册从数据源
		List<DataSourceConfigItem> slaveDatasourceConfigItems = this.dynamicDataSource.getDalConfig()
				.getDataSourceConfig().getSlaveDataSourceConfigs();
		for (DataSourceConfigItem slaveDatasourceConfigItem : slaveDatasourceConfigItems) {
			this.registerDataSourceBean(slaveDatasourceConfigItem);
		}
	}

	public void initDynamicDataSource() {
		DataSourceConfig datasourceConfig = this.dynamicDataSource.getDalConfig().getDataSourceConfig();
		Map<Object, Object> targetDataSources = new HashMap<>();
		// Master
		targetDataSources.put(DynamicDataSource.MASTER_NAME,
				applicationContext.getBean(datasourceConfig.getMasterDatasourceConfig().getId()));

		// Slave
		if (datasourceConfig.getSlaveDataSourceConfigs() != null) {
			for (int i = 0; i < datasourceConfig.getSlaveDataSourceConfigs().size(); i++) {
				targetDataSources.put(DynamicDataSource.SLAVE_PREFIX + i,
						applicationContext.getBean(datasourceConfig.getSlaveDataSourceConfigs().get(i).getId()));
			}
		}

		this.dynamicDataSource.setTargetDataSources(targetDataSources);
		this.dynamicDataSource
				.setDefaultTargetDataSource(applicationContext.getBean(datasourceConfig.getDefauleDataSource()));

		// 设置负载均衡
		if (LoadBalanceType.Random.equals(this.dynamicDataSource.getSlaveBalance())) {
			this.dynamicDataSource
					.setSlaveLoadBalanceBean(new RandomSlaveLoadBalance(this.dynamicDataSource.getDalConfig()));

		} else if (LoadBalanceType.ConsistentHash.equals(this.dynamicDataSource.getSlaveBalance())) {
			this.dynamicDataSource
					.setSlaveLoadBalanceBean(new ConsistentHashSlaveLoadBalance(this.dynamicDataSource.getDalConfig()));
		} else {
			throw new IllegalArgumentException("未知的SlaveLoadBalance类型:" + this.dynamicDataSource.getSlaveBalance());
		}

		// 设置路由选择器
		this.dynamicDataSource.setDataSourceRouter(new DefaultDataSourceRouter(this.dynamicDataSource.getDalConfig(),
				this.dynamicDataSource.getSlaveLoadBalanceBean()));
	}

	private void registerDataSourceBean(DataSourceConfigItem datasourceConfigItem) {
		DefaultListableBeanFactory beanFactory = (DefaultListableBeanFactory) applicationContext
				.getAutowireCapableBeanFactory();
		BeanDefinitionBuilder beanBuilder = BeanDefinitionBuilder
				.rootBeanDefinition(datasourceConfigItem.getDataSourceClass());

		// 设置init-method，close-method
		if (!StringUtils.isEmpty(datasourceConfigItem.getInitMethod())) {
			beanBuilder.setInitMethodName(datasourceConfigItem.getInitMethod());
		}
		if (!StringUtils.isEmpty(datasourceConfigItem.getDestroyMethod())) {
			beanBuilder.setDestroyMethodName(datasourceConfigItem.getDestroyMethod());
		}

		// 设置连接池属性配置
		for (Map.Entry<String, String> entry : datasourceConfigItem.getPoolPropertys().entrySet()) {
			beanBuilder.addPropertyValue(entry.getKey(), entry.getValue());
		}

		// 建数据源bean，注入到spring容器中
		beanFactory.registerBeanDefinition(datasourceConfigItem.getId(), beanBuilder.getBeanDefinition());
	}


}
